package ch.exm.storm.conditions;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import ch.exm.storm.query.relational.RelationalQuery;

public abstract class AbstractCompositeCondition extends AbstractCondition implements CompositeCondition, Iterable<Condition> {

	protected List<Condition> conditions = new ArrayList<Condition>();
	
	protected List<Condition> nonRelationalConditions = new ArrayList<Condition>();
	
	protected AbstractCompositeCondition(Condition... conditions) {
		for (Condition condition : conditions) addCondition(condition);
	}
	
	protected AbstractCompositeCondition(Iterable<Condition> conditions) {
		for (Condition condition : conditions) addCondition(condition);
	}

	public CompositeCondition addCondition(Condition condition) {
		conditions.add(condition);
		return this;
	}

	public Iterator<Condition> iterator() {
		return conditions.iterator();
	}
	
	@Override
	public boolean evaluateRelationalCondition(RelationalQuery query, boolean inverse) {
		nonRelationalConditions.clear();
		whereCondition = new StringBuilder();
		boolean firstCondition = true;
		
		for (Condition condition : this) {
			int lengthBeforeEvaluation = whereCondition.length();
			if (firstCondition) firstCondition = false;
			else whereCondition.append(getConditionKeyword());
			
			if (!condition.evaluateRelationalCondition(query)) {
				if (isShortcutCondition()) {
					nonRelationalConditions.add(this);
					whereCondition.setLength(0);
					return false;
				}
				nonRelationalConditions.add(condition);
				whereCondition.setLength(lengthBeforeEvaluation);
			}
			
			if (inverse) whereCondition.append("!");
			if (inverse || condition instanceof CompositeCondition) whereCondition.append("(");
			whereCondition.append(condition.getWhereStatement());
			if (inverse || condition instanceof CompositeCondition) whereCondition.append(")");
		}
		return true;
	}
	
	public Condition getNonRelationalCondition() {
		if (nonRelationalConditions == null || nonRelationalConditions.isEmpty()) return null;
		Condition nonRelationalCondition = new And(nonRelationalConditions);
		return nonRelationalCondition;
	}

	public Set<Class<?>> getQueriedClasses() {
		Set<Class<?>> queriedClasses = new HashSet<Class<?>>();
		for (Condition condition : this) queriedClasses.addAll(condition.getQueriedClasses());
		return queriedClasses;
	}

	protected abstract String getConditionKeyword();
	
	protected abstract boolean isShortcutCondition();

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		boolean firstCondition = true;
		for (Condition condition : this) {
			if (firstCondition) firstCondition = false;
			else whereCondition.append(getConditionKeyword());
			sb.append(condition.toString());
		}
		return sb.toString();
	}
}
